#ifndef cathlibcpp_bitset_H
#define cathlibcpp_bitset_H

// File:       bitset.h
// Author:     (c) Miles Sabin, 1996
// Purpose:    ANSI C++ bitset template


#ifndef included_stddef_H
#define included_stddef_H
#include <stddef.h>                   // for size_t
#endif

#ifndef cathlibcpp_bool_H
#include "bool.h"
#endif

#ifndef cathlibcpp_config_H
#include "config.h"
#endif

#ifndef cathlibcpp_stringfwd_H
#include "stringfwd.h"
#endif

#ifndef cathlibcpp_utility_H
#include "utility.h"
#endif


template<size_t N>
class bitset_reference;


template<size_t N>
class bitset
{
  public:

    // constructors
    inline bitset();
    inline bitset(unsigned long val);
    bitset(string const& str, size_t pos = 0, size_t n = size_t(-1));

    //accessors
    size_t count() const;
    inline size_t size() const;

    inline bool test(size_t pos) const;
    bool any() const;
    inline bool none() const;

    // non mutating operators ... WP says mfns: why?
    bitset<N> operator<<(size_t pos) const;
    bitset<N> operator>>(size_t pos) const;

    bitset<N> operator~() const;

    bool operator==(bitset<N> const& rhs) const;
    inline bool operator!=(bitset<N> const& rhs) const;

    // mutators
    inline bitset_reference<N> operator[](size_t pos);

    bitset<N>& operator&=(bitset<N> const& rhs);
    bitset<N>& operator|=(bitset<N> const& rhs);
    bitset<N>& operator^=(bitset<N> const& rhs);
    bitset<N>& operator<<=(size_t pos);
    bitset<N>& operator>>=(size_t pos);

    bitset<N>& set();
    inline bitset<N>& set(size_t pos, int val = 1);

    bitset<N>& reset();
    inline bitset<N>& reset(size_t pos);

    bitset<N>& flip();
    inline bitset<N>& flip(size_t pos);

    // explicit conversions
    inline unsigned long to_ulong() const;
    string to_string() const;

  private:

    void clear_overflow();

    unsigned long array_[(N+31)>>5];
};


template<size_t N>
class bitset_reference
{
  friend class bitset<N>;

  public:

    // constructors
    inline ~bitset_reference();

    // accessors
    inline bool operator~() const;

    // mutators
    inline bitset_reference<N>& operator=(bitset_reference<N> const& rhs);
    inline bitset_reference<N>& operator=(bool rhs);

    bitset_reference& flip();

    // implicit conversion
    inline operator bool() const;

  private:

    bitset_reference(bitset<N>& base, size_t pos);

    bitset<N>& base_;
    size_t pos_;
};


// Implementation of bitset<N>

template<size_t N>
inline bitset<N>::bitset()
  { reset(); }

template<size_t N>
inline bitset<N>::bitset(unsigned long val)
  {
    reset();
    array_[0] = val;
  }

template<size_t N>
inline size_t bitset<N>::size() const
  { return N; }

template<size_t N>
inline bool bitset<N>::test(size_t pos) const
  { return (((array_[pos>>5])>>(pos&31))&1) == 1; }

template<size_t N>
inline bool bitset<N>::none() const
  { return !any(); }

template<size_t N>
inline bool bitset<N>::operator!=(bitset<N> const& rhs) const
  { return !(*this == rhs); }

template<size_t N>
inline bitset_reference<N> bitset<N>::operator[](size_t pos)
  { return bitset_reference<N>(*this, pos); }

template<size_t N>
inline bitset<N>& bitset<N>::set(size_t pos, int val)
  {
    unsigned long& word = array_[pos>>5];
    unsigned long bit = 1<<(pos&31);

    if(val == 1)
      word |= bit;
    else
      word &= ~bit;

    return *this;
  }

template<size_t N>
inline bitset<N>& bitset<N>::reset(size_t pos)
  {
    array_[pos>>5] &= ~(1<<(pos&31));
    return *this;
  }

template<size_t N>
inline bitset<N>& bitset<N>::flip(size_t pos)
  {
    array_[pos>>5] ^= (1<<(pos&31));
    return *this;
  }

template<size_t N>
inline unsigned long bitset<N>::to_ulong() const
  { return array_[0]; }


// Implementation of bitset_reference<N>

template<size_t N>
inline bitset_reference<N>::~bitset_reference()
  {}

template<size_t N>
inline bool bitset_reference<N>::operator~() const
  { return !*this; }

template<size_t N>
inline bitset_reference<N>& bitset_reference<N>::operator=(bitset_reference<N> const& rhs)
  {
    base_.set(pos_, rhs);
    return *this;
  }

template<size_t N>
inline bitset_reference<N>& bitset_reference<N>::operator=(bool rhs)
  {
    base_.set(pos_, rhs);
    return *this;
  }

template<size_t N>
inline bitset_reference<N>& bitset_reference<N>::flip()
  {
    base_.flip(pos_);
    return *this;
  }

template<size_t N>
inline bitset_reference<N>::operator bool() const
  { return base_.test(pos_); }

#endif
